#pragma once
#ifndef _BASEEXCEPTION_H
#define _BASEEXCEPTION_H

#include <map>
#include <string>
#include <sstream>


#ifndef CTHROW
#define CTHROW(errClass, errCode) throw BaseException::createError< errClass > (errCode, L"", __FILE__, __LINE__)
#endif
#ifndef CTHROW_INFO
#define CTHROW_INFO(errClass, errCode, errInfo) throw BaseException::createError< errClass > (errCode, errInfo, __FILE__, __LINE__)
#endif

class BaseException
{
private:
  std::map< short, std::string >::iterator _errInfo;
  std::wstring                              _errAdditionalInfo;
  std::string                              _errFile;
  int                                      _errLine;

  void _setError (short errCode, std::wstring errInfo, std::string errFile, int errLine)
  {
    _errInfo = __errMap.find (errCode);
    if (_errInfo == __errMap.end ())
      _errInfo = __errMap.find (UNKNOWN_ERROR);
    _errAdditionalInfo = errInfo;
    _errFile = errFile;
    _errLine = errLine;
  }

protected:
  std::map< short, std::string > __errMap;

  BaseException ()
  {
    __errMap.insert (std::make_pair (UNKNOWN_ERROR, "Unknow Error"));
  }

public:
  // Define errorCodes
  static const short UNKNOWN_ERROR = 0x0000;

  // ************************************
  // Method:  BaseException::getErrorCode
  // Usage:   get error code of this error
  // Access:  public 
  // Returns: short
  // ************************************
  short getErrorCode () const
  {
    return _errInfo->first;
  }

  // ************************************
  // Method:  BaseException::getErrorDetial
  // Usage:   get error detail in string of this error
  // Access:  public 
  // Returns: std::string
  // ************************************
  std::wstring getErrorDetial () const
  {
    char strLine[11];
    char strErrorCode[7];
    sprintf_s (strLine, "%d", _errLine);
    sprintf_s (strErrorCode, "0x%4X", _errInfo->first);
    std::wostringstream wsout;
#ifdef _DEBUG
    wsout << "An error occurred at File " << _errFile.c_str ()
      << ", Line " << strLine << ": " << _errInfo->second.c_str ()
      << ": " << _errAdditionalInfo
      << ". Error Code: " << strErrorCode << ".";
#else
    wsout << "An error occurred! Code: " << strErrorCode
      << ". Detail: " << _errInfo->second.c_str () << ": " << _errAdditionalInfo << ".";
    
#endif
    return wsout.str ();
  }

  template< class T >
  static BaseException* createError (short errCode, std::wstring errInfo,
    std::string errFile, int errLine)
  {
    BaseException* newError = (BaseException*) new T ();
    newError->_setError (errCode, errInfo, errFile, errLine);
    return newError;
  }

};

#endif